#!/usr/bin/python
# -*- coding:UTF-8 -*-
 
#修改时间：2021年5月31日12:33:43
#文件名 pd-update3.py  机台升级
import socket,struct
import pandas as pd
import time,datetime
from PyQt5 import QtCore
from capture import view_loop
from PyQt5 import QtCore, Qt
from queue import PriorityQueue, Queue
import sys
import os
#from dvp import *                                           #将对应操作系统的dvp.pyd或dvp.so放入python安装目录下的Lib目录或者工程目录
#import numpy as np                                          #用pip命令安装numpy库
#import cv2  
 
 
pd.set_option('display.max_columns', None)#显示所有列
pd.set_option('display.max_rows', None)   #显示所有行
# pd.set_option('max_colwidth',100)       #设置value的显示长度为100，默认为50



class FinsSocketClass(QtCore.QObject):
    """
    used:
    Omron Fins socket com.V0.0.1 for used CP1H-EX CPU. Writed by hongpu shanghai changning 2021/4/18
    You can used the word,bit.
    remark:
    REAL32 有效数据位2位;REAL64 有效数据17位
    """
    sin_s=QtCore.pyqtSignal(object)
    sin_s_ok=QtCore.pyqtSignal(object)
 
    def __init__(self, ip='192.168.250.10',ip_plc='192.168.250.1',port=9600):
        self.ip = ip
        self.ip_plc = ip_plc
        self.port=port
 
        self.s,self.s_ok,self.s_coning ,self.com_err= None,False,False,False
 
        super(FinsSocketClass,self).__init__()
 
        _python_type=['h', 'i', 'q', 'H', 'I', 'Q', 'f', 'd', 's','?']
        _plc_type=['INT16','INT32','INT64','UINT16','UINT32','UINT64','REAL32','REAL64','STR','BOOL']
        self.data_type = pd.Series(data=_python_type,index=_plc_type)
 
    def __del__(self):
        self.ip= ''
        self.ip_plc = ''
        self.port=9600
 
        self.s,self.s_ok,self.s_coning,self.com_err = None,False,False,False
        # self.sin_s.emit(None);self.sin_s_ok.emit(False)
        self.close_socket()
 
    def __try_connect(self,try_conMode=False):
        """
        :param try_conMode: True mean test the fins connect ok or ng.
        :return: None
        """
        try:
            s=socket.socket(socket.AF_INET,socket.SOCK_STREAM )
            if try_conMode:s.settimeout(1)
            # self.s.setblocking(False)#非阻塞模式
            s.connect((self.ip_plc,9600))
            if try_conMode:
                s.getpeername()#('192.168.115.33', 9600)
                s.close()
            else:
                self.s, self.s_ok, self.s_coning, self.com_err = s,True, False, False
                self.sin_s.emit(s);self.sin_s_ok.emit(True)
            return None
        except Exception as e:
            self.s, self.s_ok=None, False
            self.sin_s.emit(None);self.sin_s_ok.emit(False)
            return -1
 
    def __connect(self,timeout=0):
        """
        used:connect the server used the default value.
        :param timeout: >0 choose the auto connect after the err generate.
        :return:None
        """
        if not self.__try_connect(try_conMode=True):
            return self.__try_connect(try_conMode=False)
        else:
            if timeout <= 0:print('Connect Err!Please connect after hand check.') ;return -1
            print('Connect Err!Auto connect start...')
 
            while True:#not self.s_ok
                self.s_coning = True
                time.sleep(timeout)
                print('等待PLC连接......')
                if not self.__try_connect(try_conMode=True):
                    if not self.__try_connect(try_conMode=False):
                        self.s_coning = False;self.com_err=False
                        break
                
            return None
 
    def close_socket(self):
        try:
            self.s.shutdown(socket.SHUT_RDWR)
            self.s.close()
            a = None
        except Exception as e:
            a = -1
        finally:
            self.s = None;self.s_ok = False
            # self.sin_s.emit(None);self.sin_s_ok.emit(False)
            return a
 
    def __node(self,ip=''):#From the ip address to _node NO. 2 bit hex str
        nodeStr = hex(int(ip.split('.')[3]))[2:]
        return ('0'+nodeStr)[-2:]
 
    def __existErr(self,BackValue=b'',shakeHandMode=False):
        if len(BackValue) <16:
            return True
        v = BackValue
        b=(v[12] == 0) and (v[13] == 0) and (v[14] == 0) and (v[15] == 0)
 
        if not shakeHandMode:  #read write fins
            if len(BackValue) <30:return True
            b =  b and (v[28] == 0) and (v[29] == 0)
 
        return  not b
 
    def con(self,timeout=1):   #timeout >0 auto connect mode
        """
        :param timeout: auto connect Mode :set timeout time
        :return: socket object or None
        """
        if self.__connect(timeout=timeout): return None#Have error,Not connect
        shakeHandsStr='46494E530000000C0000000000000000000000'
        shakeHandsStr = shakeHandsStr+self.__node(self.ip)
 
        try:
            self.s.send(bytes.fromhex(shakeHandsStr))
            BackValue=self.s.recv(1024)
            a=-1 if self.__existErr(BackValue,True) else None
        except Exception as e:a=-1
        finally:
            if a==-1:
                self.sin_s.emit(None);self.sin_s_ok.emit(False)
                self.s, self.s_ok = None, False
            return -1 if a else None
 
    def __dataType_n(self, data_type=''):#word 16 bit =1
        offset = 1
        if (data_type == 'h') or (data_type == 'H') or (data_type == 's'):
            offset = 1
        elif (data_type == 'i') or (data_type == 'I') or (data_type == 'f') :
            offset = 2
        elif (data_type == 'q') or (data_type == 'Q') or (data_type == 'd'):
            offset = 4
        return offset
 
    def __dec_to_hex_n(self,data=None,strOrderMode=False):#1 byte
        """
        :param data:
        :param strOrderMode: 2 pcs str equal 1 byte.Having 3 mode
                             mode1: data length;mode2:str number;mode3:hex number
        :return:str: hex str n
        """
        if isinstance(data,str):
            str_n = '0000' + hex(int(len(data)/2) if strOrderMode else int(data))[2:]
        else:
            str_n = '0000' + hex(data)[2:]
        return str_n[-4:].upper()
 
    def __adrWordBit_map(self,address='D0'):
        """
        Used:Split the address like 'D0' to address ('D') ,word('0'),bit('00')
        :param address: str
        :return: str normal addreass of PLC map fins to communication.
        """
        lst = address.split('.')
        exist_bit = (False if address.find('.') == -1 else True)
        if len(lst[0])<2:return '';adr=''
 
        word_=self.__dec_to_hex_n(lst[0][1:])
        word_bit = ('00'+self.__dec_to_hex_n(lst[1]))[-2:] if exist_bit else '00'
 
        if lst[0][0]=='D':adr='82';word_bit= '00'  # word_+bit
        elif lst[0][0]=='H':adr='B2' if not exist_bit else '32'
        elif lst[0][0] == 'W':adr = 'B1' if not exist_bit else '31'
        elif len(lst)>=4:
            if lst[0][0:3] == 'CIO':
                adr = 'B0' if not exist_bit else '30';
                word_ = self.__dec_to_hex_n(lst[0][3:])
            else:adr='';word_='';word_bit=''
        else:adr='';word_='';word_bit=''
        return  adr+word_+word_bit# adr+word_+bit
 
    def __strOrder(self,address='D0',length=0,data_type='',read_model=False,data=None):
        offset=self.__dataType_n(data_type)
        read_write='0101' if read_model else '0102'
 
        str1='000000020000000080000200'+self.__node(self.ip_plc)+'0000'
        str1 = str1 + self.__node(self.ip) + '0000' + read_write + self.__adrWordBit_map(address)# word+bit
        str1=str1+self.__dec_to_hex_n(length*offset)          # data length
 
        if read_model==False:
            str1=str1+self.__data_to_hex(data,data_type)      # data
        return '46494E530000'+self.__dec_to_hex_n(str1,True)+str1
 
    def __TypeExchange(self,data=b''):
        n=len(data)
        barr=bytearray(n)
        for i in range(0,n,2):
            barr[i] = data[i+1]
            barr[i+1] = data[i]
        return barr
 
    def __data_to_hex(self,data=None,data_type=''):
        lst=[]
        if data:
            for i in data:
                b1=struct.pack(data_type,i)#byte
                b = (data_type != self.data_type.BOOL) and (data_type != self.data_type.STR)
                b1=(self.__TypeExchange(b1) if b  else b1)
                lst.append(b1)
 
            if (data_type == self.data_type.BOOL): lst.reverse()
        b1=b''.join(lst)
        return b1.hex()
 
    def __byte_decode(self,data=b'',data_type='',length=0):
        b=(data_type!=self.data_type.BOOL) and (data_type!=self.data_type.STR)
        b_arr=(self.__TypeExchange(data) if b else data)
        fmt = str(int(length)) + data_type
 
        return struct.unpack(fmt, b_arr)
 
    def __DataAdr(self,address='D0',length=0,data_type=''):
        """
        used:Produce the writed address of PLC
        :param address: str
        :param length: int
        :param data_type: str
        :return: list
        """
        offset=self.__dataType_n(data_type)
 
        adr='';bit_str=''
        exist_bit = (False if address.find('.') == -1 else True)
        if exist_bit:
            if len(address)<2:return []
            adr=address.split('.')[0];bit_str=address.split('.')[1]
        else:adr=address
 
        if exist_bit:
            adr_name='' ;adr_start_number=0;bit_start_number=0;tmp=[]
            if adr[0]=='C':
                if len(adr)<4:return []
                adr_name=adr[0:3];adr_start_number=int(adr[3:])
            else:
                adr_name=adr[0] ; adr_start_number=int(adr[1:])
 
            bit_start_number=int(bit_str)
            if bit_start_number >= 16: return []
            n1=adr_start_number;n2=bit_start_number;
 
            for i in range(length):
                tmp.append(adr_name+str(n1)+'.'+('00'+str(n2))[-2:])
                n2+=1
                if n2>=16:n2=0;n1+=1
 
            return tmp
        else:
            if adr[0] =='C':
               if len(adr)<=3:return []
               n0=int(adr[3:])
               return [adr[0:3]+'.'+(('00' + str(i))[-2:]) for i in range(n0, n0 + length*offset,offset)]
            else:
                if len(adr)<=1:return []
                n0=int(adr[1:])
 
                return [adr[0:1]+str(i) for i in range(n0, n0 + length*offset,offset)]
 
    def exist_con(self):
        try:
            return self.s.getpeername()
        except Exception as e:
            return None
 
 
 
    # =============================================================================================
    def readData(self,address='D0',length=0,data_type='',back_s=False):
        """
        :param address: address format D+Number; D:address name;Number:decimal.
        :param length: length int >0;number is 1,2,4 (16,32,64) int or float.
                You alse can use the str.1 word 16bit
        :param data_type:str
        :return: None or tupe or str Series
        """
        backEmptyValue=pd.Series() if back_s else None
 
        strOrder = self.__strOrder(address, length, data_type, True)
        try:
            self.s.send(bytes.fromhex(strOrder))
            BackValue = self.s.recv(1024)  #b'FINS\x00\x00\...'
        except Exception as e:
            self.sin_s.emit(None);self.sin_s_ok.emit(False)
            self.s=None;self.s_ok=False
            return backEmptyValue
 
        if (len(BackValue)<=30) or self.__existErr(BackValue):
            self.com_err=True
            return backEmptyValue
 
        data=self.__byte_decode(data=BackValue[30:], data_type=data_type,length=length)
        data=list(data)
        data_adr=self.__DataAdr(address=address, length=length, data_type=data_type)
 
        # return data_adr if back_s else data
        # if (data_type=='?') and (back_s==False):data.reverse()#bool data left is high bit
        return (pd.Series(data, index=data_adr) if back_s else data)
    # =============================================================================================
    def writeData(self,address='D100',data=None,data_type=''):
        """
        :param address: str
        :param data: list decimal
        :return:None or -1
        """
        a=None
        if type(data)==type(pd.Series()):data=data.tolist()
        if isinstance(data,(str,int,float,bytes)):data=[data]
        # print('222222222222============',address,data,data_type)
        strOrder = self.__strOrder(address,len(data),data_type,False,data)#46494E53...
        # print('222222222222============',strOrder)
        try:
            self.s.send(bytes.fromhex(strOrder))
            BackValue = self.s.recv(1024)  #b'FINS\x00\x00\x00\...'#46494e5300000016000...
        except Exception as e:
            self.sin_s.emit(None);self.sin_s_ok.emit(False)
            self.s = None;self.s_ok = False;a=-1
            return a
 
        if  self.__existErr(BackValue):
            a=-1;self.com_err=True;#errOccur=True
        return a
 
    def rw_socket(self, adr='', data_or_len=[], data_type='?', r_mode=False,back_s=False):
            if r_mode:
                return self.readData(address=adr, length=data_or_len,data_type=data_type,back_s=back_s)
            else:
                return self.writeData(address=adr,data=data_or_len,data_type=data_type)

def test_fins_socket(ip='10.1.68.147', ip_plc='10.1.68.145'):  #层后 229 227
    s = FinsSocketClass(ip, ip_plc)
    # print(s.fileno())  # 212
    s.con()#默认发生错误自动连接，设置为0取消自动连接
    print("连接成功！")
    #test_read_bit(s)
    #test_write_bit(s)
 
    #el_read_D(s) #读取拍照标识符寄存器
    #vi_read_D(s)
    #print(el_read_D(s))
    write_zero(s)
    #通知第一次拍照时
    while True:
        #time.sleep(1)
        v = vi_read_D(s)
        if v ==[17408]:

            #拍照完成后置位
            #相机拍照代码--------
            print("读取到第一次WG拍照寄存器标识符，准备开始拍照...")
            start_time = time.time()
            for i in [0,3,2,1]:
                view_loop(i,0)
            time.sleep(1.3)
            print("第一次VI拍照完成！")
            end_time=time.time()
            print("拍照时间:",end_time-start_time)
            write_VI_Done(s)
            #time.sleep(0.8)
            write_zero(s)
            
        
        if v ==[17664]:
            #相机拍照代码--------
            print("读取到第二次WG拍照寄存器标识符，准备开始拍照...")
            start_time = time.time()
            #time.sleep(3)
            for i in [0,3,2,1]:
                view_loop(i,1)
            time.sleep(1.3)
            print("第二次VI拍照完成")
            end_time=time.time()
            print("拍照时间:",end_time-start_time)
            write_VI_Done(s)
            #time.sleep(0.8)
            write_zero(s)
            
        if v ==[17920]:
            #相机拍照代码--------
            print("读取到第三次WG拍照寄存器标识符，准备开始拍照...")
            start_time = time.time()
            for i in [0,3,2,1]:
                view_loop(i,2)
            time.sleep(1.3)
            print("第三次VI拍照完成")
            end_time=time.time()
            print("拍照时间:",end_time-start_time)
            write_VI_Done(s)
            write_zero(s)


        #读取到OK与NG信号

    s.close_socket()

def write_zero(s):
    
    print('D800.writed data=', s.writeData('D800', 0, s.data_type.UINT16))

def vi_read_D(s):
    #读D800的寄存器值
    print('D800 read data=', s.readData('D800',1, s.data_type.UINT16))
    b=s.readData('D800',1, s.data_type.UINT16)
    return b
 
def write_VI_Done(s):
    #外观拍照完成
    VI_done = [0x3231]
    #VI_done2 = [0x3231]
    print('D350.writed data=', s.writeData('D350', VI_done, s.data_type.UINT16))
    print('D375.writed data=', s.writeData('D375', VI_done, s.data_type.UINT16))
 

 

 
def test_read_S(s):
    pass
    # print('3.6.read data=',s.readData('D50',2,s.data_type.STR))#待完善
 
if __name__=='__main__':
 
    
 
    global s_global_test
    q_test = Queue(1)
 
    class TestThread1(QtCore.QThread):
        def __init__(self):
            super(TestThread1, self).__init__()
            self.s_global_test = FinsSocketClass(ip='10.1.68.146', ip_plc='10.1.68.145')
            self.s_global_test.con()
 
        def run(self):
            global s_global_test
            while 1:
                #test_fins_socket2()
                #print('Thread1')

                if not self.s_global_test.s_ok:
                    self.s_global_test = FinsSocketClass()
                    self.s_global_test.con()
                    s_global_test = self.s_global_test
                    if q_test.empty():
                        q_test.put(self.s_global_test)
                    while not self.s_global_test.s_ok: pass
                else:
                    s_global_test = self.s_global_test
                    if q_test.empty():
                        q_test.put(self.s_global_test)
                    while self.s_global_test.s_ok: pass
                #self.sleep(1)
    #==========================================================
    class TestThread2(QtCore.QThread):
        def __init__(self):
            super(TestThread2, self).__init__()
            self.s_global_test = None
 
        def run(self):
            global s_global_test
            while 1:
                #test_fins_socket()
                print('读取数据线程Thread2')
                """             #全局变量
                if s_global_test:
                    if s_global_test.s_ok:
                         test_read_bit(s_global_test)
                """
                if not q_test.empty():  # 队列
                    ss = q_test.get()
                #self.sleep(0.25)
    #==========================================================
    
 
    #基础类测试
    #test_fins_socket()
 
    #线程中测试
    """  def test2():
        app = Qt.QApplication(sys.argv)
        #t1=TestThread1()
        t2=TestThread2()
        #t1.start()
        t2.start()
 
        sys.exit(app.exec())
 
    test2() """
    #while True:
    try:
        os.mkdir("D:/dic")
    except FileExistsError:
        pass
    test_fins_socket()
#==========================================================================================